<?php

namespace Wgx\Admin\Com;

use Wgx\Admin\Entity\AdminMenuEntity;
use Wgx\Admin\Entity\AdminToMenuEntity;
use Wgx\PdoEx\QueryBuilder;

final class AdminMenuRightsUtil
{
    /** @var null|AdminMenuRightsUtil */
    private static $singleObj = null;

    /**
     * 保存用户的菜单数据，init 时设置
     * [id1=>AdminMenuEntity1, id2=>AdminMenuEntity2, ...]
     * @var null|array
     */
    private $menuId2MenuMap = null;
    /**
     * 默认不会给 $menuId2MenuMap 中的 AdminMenuEntity 设置上 patent 关联，
     * 只有在需要的时候才会设置 patent 关联, 这时就又需要一个指示设没设置 patent 关联的标志
     * @var bool
     */
    private $isSetParentDone = false;
    /**
     * 用于校验用户操作的权限, init 时和$menuId2MenuMap同步设置
     * [path1=>intval(rights1), path2=>intval(rights2), ...]
     * @var null|array
     */
    private $path2RightsMap = null;
    /**
     * 用于设置面包屑, init 时和$menuId2MenuMap同步设置
     * [path1=>AdminMenuEntity1, path2=>AdminMenuEntity2, ...]
     * @var null|array
     */
    private $path2MenuMap = null;
    /**
     * 计算出 currentActiveMenuMap 后保存在这个变量中，避免再次计算
     * [id1=>AdminMenuEntity1, id2=>AdminMenuEntity2, ...]
     * @var array
     */
    private $currentActiveMenuMap;
    /**
     * 用于渲染后台菜单栏, 在需要渲染后台菜单时才会设置
     * @var null|MenuBarMenuEntity[]
     */
    private $menuBarLevel1Arr = null;

    /**
     * @return AdminMenuRightsUtil|null
     */
    public static function getSingleObj()
    {
        if (empty(self::$singleObj)) {
            self::$singleObj = new AdminMenuRightsUtil();
            self::$singleObj->init();
        }
        return self::$singleObj;
    }

    private function __construct()
    {
    }

    private function init()
    {
        $adminUserEntity = AdminSession::getSingleObj()->getAdminUserEntity();
        if (empty($adminUserEntity)) {
            return;
        }

        $table = AdminToMenuEntity::getSingle()->getTable() . ' AS r JOIN ' . AdminMenuEntity::getSingle()->getTable() . ' AS m ON r.menu_id = m.id';
        $where = null;
        $adminIds = $adminUserEntity->getVirtualIds();
        if ($adminIds) {
            $adminIds[] = $adminUserEntity->getId();
            $where = array('r.admin_id' => array('in' => $adminIds));
        } else {
            $where = array('r.admin_id' => $adminUserEntity->getId());
        }

        $result = QueryBuilder::useDb(AdminToMenuEntity::getSingle()->getConnName())->select(
            $table, 'r.rights AS rr, m.*', $where, null, array('m.pid' => 'asc', 'm.priority' => 'desc', 'm.id' => 'asc')
        )->getPdoStatement()->fetchAll(\PDO::FETCH_ASSOC);
        if (empty($result)) {
            return;
        }

        $this->menuId2MenuMap = [];
        $this->isSetParentDone = false;
        $this->path2RightsMap = [];
        $this->path2MenuMap = [];
        foreach ($result as $item) {
            $entity = new AdminMenuEntity($item);
            $id = intval($entity->getId());
            $path = $entity->getPath();
            $path = empty($path) ? null : trim($path);
            if (empty($this->menuId2MenuMap[$id])) {
                $this->menuId2MenuMap[$id] = $entity;
                if ($path) {
                    $this->path2MenuMap[$path] = $entity;
                }
            }

            if ($path) {
                $rights = intval($item['rr']);
                $existRights = empty($this->path2RightsMap[$path]) ? 0 : intval($this->path2RightsMap[$path]);
                $this->path2RightsMap[$path] = $existRights | $rights;
            }
        }
    }

    private function setParent()
    {
        if ($this->isSetParentDone) {
            return;
        }

        if ($this->menuId2MenuMap) {
            /** @var AdminMenuEntity $entity */
            foreach ($this->menuId2MenuMap as $id => $entity) {
                $pid = intval($entity->getPid());
                if (empty($pid) || empty($this->menuId2MenuMap[$pid])) {
                    continue;
                }
                $entity->setParentEntity($this->menuId2MenuMap[$pid]);
            }
        }
        $this->isSetParentDone = true;
    }

    /**
     * 用于校验用户操作的权限
     * [path1=>intval(rights1), path2=>intval(rights2), ...]
     * @return array|null
     */
    public function getPath2RightsMap()
    {
        return $this->path2RightsMap;
    }

    /**
     * 当前用户对 $path 拥有的权限
     * @param string $path
     * @return int
     */
    public function getPathRights($path)
    {
        $path = empty($path) ? null : trim($path);
        if (empty($path)) {
            return AdminToMenuEntity::RightNone;
        }
        if (empty($this->path2RightsMap)) {
            return AdminToMenuEntity::RightNone;
        }
        return empty($this->path2RightsMap[$path]) ? AdminToMenuEntity::RightNone : $this->path2RightsMap[$path];
    }

    /**
     * 当前用户 对 当前请求 $path 拥有的权限
     * @return int
     * @throws \Exception
     */
    public function getCurrentRights()
    {
        if (empty($this->path2RightsMap)) {
            return AdminToMenuEntity::RightNone;
        }
        $path = \PhpYes\Apps::getReqPath();
        return empty($this->path2RightsMap[$path]) ? AdminToMenuEntity::RightNone : $this->path2RightsMap[$path];
    }

    /**
     * 用于设置面包屑
     * [path1=>AdminMenuEntity1, path2=>AdminMenuEntity2, ...]
     * @return array|null
     */
    public function getPath2MenuMap()
    {
        if (!$this->isSetParentDone) {
            $this->setParent();
        }
        return $this->path2MenuMap;
    }

    /**
     * 当前请求的菜单
     * @return AdminMenuEntity|null
     * @throws \Exception
     */
    public function getCurrentMenu()
    {
        $path2MenuMap = $this->getPath2MenuMap();
        if (empty($path2MenuMap)) {
            return null;
        }
        $path = \PhpYes\Apps::getReqPath();
        return empty($path2MenuMap[$path]) ? null : $path2MenuMap[$path];
    }

    /**
     * 当前 active 菜单
     * [id1=>AdminMenuEntity1, id2=>AdminMenuEntity2, ...]
     * @return array
     * @throws \Exception
     */
    public function getCurrentActiveMenuMap()
    {
        if (empty($this->currentActiveMenuMap)) {
            $this->currentActiveMenuMap = array();
            $m = $this->getCurrentMenu();
            while ($m) {
                $this->currentActiveMenuMap[intval($m->getId())] = $m;
                $m = $m->getParentEntity();
            }
        }
        return $this->currentActiveMenuMap;
    }

    /**
     * 用于渲染后台菜单栏
     * @return MenuBarMenuEntity[]|null
     */
    public function getMenuBarLevel1Arr()
    {
        if (empty($this->menuBarLevel1Arr)) {
            if ($this->menuId2MenuMap) {
                $map = array();
                /** @var AdminMenuEntity $entity */
                foreach ($this->menuId2MenuMap as $id => $entity) {
                    $isInMenubar = intval($entity->getIsInMenubar());
                    if ($isInMenubar) {
                        $map[$id] = new MenuBarMenuEntity($entity->getData());
                    }
                }
                if ($map) {
                    /** @var MenuBarMenuEntity $entity */
                    foreach ($map as $id => $entity) {
                        $pid = intval($entity->getPid());
                        if (empty($pid) || empty($map[$pid])) {
                            continue;
                        }
                        $map[$pid]->addChild($entity);
                    }

                    $this->menuBarLevel1Arr = array();
                    foreach ($map as $id => $entity) {
                        $pid = intval($entity->getPid());
                        if ($pid == AdminMenuEntity::AdminMenuRootId) {
                            $this->menuBarLevel1Arr[] = $entity;
                        }
                    }
                }
            }
        }
        return $this->menuBarLevel1Arr;
    }

    public function menuHtml()
    {
        $level1MenuArr = $this->getMenuBarLevel1Arr();
        if (empty($level1MenuArr)) {
            $html = '';
        } else {
            $html = '<ul class="sidebar-menu-list">';
            foreach ($level1MenuArr as $menu) {
                $html .= $this->renderMenu($menu);
            }
            $html .= '</ul>';
        }
        return $html;
    }

    /**
     * @param MenuBarMenuEntity $menu
     * @return string
     * @throws \Exception
     */
    private function renderMenu(MenuBarMenuEntity $menu)
    {
        if (empty($menu)) {
            return '';
        }

        $menuLevel = intval($menu->getLevel());
        $children = $menu->getChildrenMap();

        $path = $menu->getPath();
        $path = empty($path) ? null : trim($path);

        if (empty($path)) {
            $a = '<a href="#" onclick="return false;">';
        } else {
            $defaultQuery = $menu->getDefaultQuery();
            $defaultQuery = empty($defaultQuery) ? null : trim($defaultQuery);
            if (empty($defaultQuery)) {
                $a = '<a href="' . $path . '">';
            } else {
                $a = '<a href="' . "{$path}?{$defaultQuery}" . '">';
            }
        }

        if ($menuLevel == 2) {
            $a .= '<i class="indicator fas fa-caret-right"></i>';
        }

        $icon = $menu->getIcon();
        $icon = empty($icon) ? null : trim($icon);
        if ($icon) {
            $iconColor = $menu->getIconColor();
            $iconColor = empty($iconColor) ? null : trim($iconColor);
            if ($iconColor) {
                $a .= '<i class="menu-content menu-icon ' . $icon . '" style="color: ' . $iconColor . ';"></i>';
            } else {
                $a .= '<i class="menu-content menu-icon ' . $icon . '"></i>';
            }
        }
        $a .= '<span class="menu-content menu-text">' . $menu->getName() . '</span>';

        // 特殊处理开始 / do special begin
        $menuId = intval($menu->getId());
        if ($menuId == 7) {
            $a .= '<i class="alert fas fa-exclamation-triangle" style="color: #DD5A43;"></i>';
        } elseif ($menuId == 10) {
            $a .= '<span class="wgxadmin-badge menu-badge" style="background-color: #428BCA; opacity: 0.88;">5</span>';
        }
        // 特殊处理结束 / do special end

        if (!empty($children)) {
            $a .= '<i class="arrow-down fas fa-angle-down"></i>';
        }
        $a .= '</a>';

        $activeMap = $this->getCurrentActiveMenuMap();

        if (empty($children)) {
            if (empty($activeMap) || empty($activeMap[$menuId])) {
                $html = '<li class="menu menu-level' . $menuLevel . '">';
            } else {
                $html = '<li class="menu active menu-level' . $menuLevel . '">';
            }
            $html .= $a;
        } else {
            if (empty($activeMap) || empty($activeMap[$menuId])) {
                $html = '<li class="group menu-level' . $menuLevel . '">';
            } else {
                $html = '<li class="group open active menu-level' . $menuLevel . '">';
            }
            $html .= $a;
            $html .= '<ul class="submenu">';
            foreach ($children as $subId => $subMenu) {
                $html .= $this->renderMenu($subMenu);
            }
            $html .= '</ul>';
        }

        $html .= '</li>';

        return $html;
    }

}